package Security;

import java.security.InvalidKeyException;
import java.security.Key;
import java.util.Random;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * Encrypts a password or other String with an AES encryption using a key and
 * salt. The salt will be placed behind the password before encrypting, and
 * pasted at the start of the encrypted password. Since it always will be 16
 * bytes, the decryptor will filter it before decrypting.
 *
 * @author Kenneth Veenstra
 * @version 1.1
 *
 */
public class EncryptorAES
{
    private static final String ALGORITHM = "AES";
    private static final byte[] keyValue = new byte[]
    {
        'd', 'k', 'r', 'Q',
        'L', 'v', 'j', 'l', 'Z', 'P', 'z', 'r', 'I', 'P', 'M', 'V'
    }; // dkrQLvjlZPzrIPMV
    private static final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz";

    /**
     * Encrypts a string value with an AES encryption.
     * Using 16 byte salt.
     *
     * @param valueToEncrypt String you want to encrypt
     *
     * @return encrypted value
     */
    public static String encrypt(String valueToEncrypt)
    {
        try
        {
            //creates the key with the key value
            Key key = generateKey();
            //generate a new salt
            String salt = generateSalt();
            //combine the value to encrypt and the salt
            String newValueToEnc = valueToEncrypt + salt;
            //Starting chipper and encryption
            Cipher c = Cipher.getInstance(ALGORITHM);
            c.init(Cipher.ENCRYPT_MODE, key);
            byte[] encValue = c.doFinal(newValueToEnc.getBytes());
            String encryptedValue = new BASE64Encoder().encode(encValue);
            //returning salt + encryptedValue
            return (salt.toString() + encryptedValue);
        }
        catch ( IllegalBlockSizeException e )
        {
            e.printStackTrace();
        }
        catch ( BadPaddingException e )
        {
            e.printStackTrace();
        }
        catch ( InvalidKeyException e )
        {
            e.printStackTrace();
        }
        catch ( Exception e )
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Decrypts an encrypted value with AES encryption.
     * Using 16 byte salt.
     *
     * @param encryptedValue an encrypted value
     *
     * @return
     */
    public static String decrypt(String encryptedValue)
    {
        try
        {
            //creates the key with the key value
            Key key = generateKey();
            //split salt and encryptedValue
            @SuppressWarnings("unused")
            String salt = encryptedValue.substring(0, 15);
            String encValue = encryptedValue.substring(16);
            //Starting chipper and decrypting the encrypted value
            Cipher c = Cipher.getInstance(ALGORITHM);
            c.init(Cipher.DECRYPT_MODE, key);
            byte[] decordedValue = new BASE64Decoder()
                    .decodeBuffer(encValue);
            byte[] decValue = c.doFinal(decordedValue);
            String decryptedValue = new String(decValue);
            //removing salt from the value
            decryptedValue = decryptedValue.substring(0, decryptedValue.length() - 16);
            //returns the value
            return decryptedValue;
        }
        catch ( IllegalBlockSizeException e )
        {
            e.printStackTrace();
        }
        catch ( BadPaddingException e )
        {
            e.printStackTrace();
        }
        catch ( InvalidKeyException e )
        {
            e.printStackTrace();
        }
        catch ( Exception e )
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Generates the key
     *
     * @return a key
     *
     * @throws Exception oops something went wrong while making the key
     */
    private static Key generateKey() throws Exception
    {
        Key key = new SecretKeySpec(keyValue, ALGORITHM);
        return key;
    }

    /**
     * Generates a random salt using lower and uppercase and numbers
     *
     * @return a random String (salt)
     */
    private static String generateSalt()
    {
        Random random = new Random();
        String salt = "";
        for ( int i = 0; i < 16; i++ )
        {
            salt = salt + characters.charAt(random.nextInt(characters.length()));
        }
        return salt;
    }

    /**
     * Test purpose
     *
     * @param args none needed
     */
    public static void main(String[] args)
    {
        System.out.println("Encrypting 'Trollface'... ->");
        String encrypted = EncryptorAES.encrypt("Trollface");
        System.out.println(encrypted);
        System.out.println("Decrypting '" + encrypted + "'... ->");
        String decrypt = EncryptorAES.decrypt(encrypted);
        System.out.println(decrypt);
    }
}
